﻿//-----------------------------------------------------------------------
// <copyright file="MLCSharpCodeTemplate.cs" company="MAPILab Ltd">
//     Copyright (c) MAPILab Ltd.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace MAPILab.SharePoint.Explorer.CodeForm.CodeTemplates
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Text;
    using MAPILab.SharePoint.Explorer.Properties;
    using MAPILab.SharePoint.Explorer.Utilities;

    /// <summary>
    /// C# code template class
    /// </summary>
    class MLCSharpCodeTemplate : MLBaseCodeTemplate
    {
        #region Methods

        /// <summary>
        /// Generate code template on C#
        /// </summary>
        /// <returns>string that contains code template</returns>
        public override string GenerateTemplate()
        {
            StringBuilder template = new StringBuilder();            
            //add using namespaces 
            foreach (string nameSpaceName in Settings.Default.namespaces)
            {
                template.Append("using " + nameSpaceName + ";" + "\r\n");
            }
            template.Append("\r\n");
            //begin class generate
            template.Append("public class Tester" + "\r\n");
            template.Append("{" + "\r\n");
            template.Append("\tstatic void Main(" + "\r\n");

            //Add Parameters
            AddMainParameters(template);

            //begin method main
            template.Append("\t\t)" + "\r\n");
            template.Append("\t{" + "\r");

            //add methods call template
            GenerateMethodsCallTemplate(template);

            template.Append("\n\t\t");

            //add template for output browser
            GenerateOutput(template);

            //end method main and class
            template.Append("\n\t}" + "\r\n");
            template.Append("}" + "\r\n");
            return template.ToString();
        }

        /// <summary>
        /// Generate template for output
        /// </summary>
        /// <param name="template">string that contains code</param>
        protected override void GenerateOutput(StringBuilder template)
        {
            template.Append("\n\t\t");
            template.Append("\n\t\t// Output browser configuration");
            template.Append("\n\t\t//browser.Text = \"Browser window\";");
            template.Append("\n\t\t//browser.DisplayMode = MAPILab.SharePoint.Explorer.Utilities.ScriptRunner.DisplayMode.Expanded;");
            template.Append("\n\t\t//browser.OpenInNewWindow = false;");
            template.Append("\n\t\tbrowser.ReturnValue = null;");
        }

        /// <summary>
        /// Add current method
        /// </summary>
        /// <param name="template">output template</param>
        /// <param name="methParam">method parameter to add</param>
        protected override void GenerateMethodCallTemplate(StringBuilder template, MLMethodParameter methParam)
        {
            MethodInfo methodInfo = methParam.MethodInfo;
            ParameterInfo[] parameters = methodInfo.GetParameters();

            template.AppendFormat("\t\t// Calling method {0}\r\n", methodInfo.Name);

            //Adding parameters for method
            List<ParameterNameAndType> paramList = AddMethodParameters(template, parameters);

            foreach (ParameterNameAndType parameterNameAndType in paramList)
            {
                template.AppendFormat("\t\t{0} {1}{2};\r\n", parameterNameAndType.TypeName, parameterNameAndType.Name, (parameterNameAndType.IsOutParameter ? "" : " = "));
            }

            if (paramList.Count > 0)
            {
                template.Append("\t\t\r\n");
            }
                
            bool nonVoidIndent = false;

            //return value
            if (!(methodInfo.ReturnType == typeof(void)))
            {
                template.AppendFormat("\t\t{0} {1} = \r\n", MLUtils.GetTypeOutput(MLUtils.GetType(methodInfo.ReturnType)), string.Format("value{0}", methodsParamCounter++));
                //nonVoidIndent = "\t";
                nonVoidIndent = true;
            }

            if (methodInfo.IsPublic)
            {
                //Add Public Method
                AddPublicMethod(template, methParam, methodInfo, paramList, nonVoidIndent);
            }
            else
            {
                //Add Non public method
                AddNonPublicMethod(template, methParam, methodInfo, paramList, nonVoidIndent);
            }

            template.Append("\t\t\r");
        }

        /// <summary>
        /// Add non public method
        /// </summary>
        /// <param name="template">output template</param>
        /// <param name="methParam">method parameter to add</param>
        /// <param name="methodInfo">adding method</param>
        /// <param name="paramList">parameters list</param>
        /// <param name="nonVoidIndent">non void indent</param>
        protected override void AddNonPublicMethod(StringBuilder template, MLMethodParameter methParam, MethodInfo methodInfo, List<ParameterNameAndType> paramList, bool nonVoidIndent)
        {
            string objectName = String.Empty;
            string ifNotVoid = String.Empty;

            if (!(methodInfo.IsStatic))
            {
                objectName = methParam.MainParamName;
            }
            else
            {
                objectName = "null";
            }

            if (nonVoidIndent)
            {
                ifNotVoid = "(" + MLUtils.GetTypeOutput(MLUtils.GetType(methodInfo.ReturnType)) + ")";
            }

            template.AppendFormat("\t\t{0}{1}{2}.Invoke({3}, ", (nonVoidIndent)?"\t":"", ifNotVoid, methParam.MainParamName.Replace("mo_", ""), objectName);

            if (paramList.Count > 0)
            {
                template.Append("new object[]{");
                GetParameters(template, paramList, nonVoidIndent, true);
                template.Append("}");
            }
            else
            {
                template.Append("null");
            }

            template.Append(");\r\n");
        }

        /// <summary>
        /// Add public method
        /// </summary>
        /// <param name="template">output template</param>
        /// <param name="methParam">method parameter to add</param>
        /// <param name="methodInfo">adding method</param>
        /// <param name="paramList">parameters list</param>
        /// <param name="nonVoidIndent">non void indent</param>
        protected override void AddPublicMethod(StringBuilder template, MLMethodParameter methParam, MethodInfo methodInfo, List<ParameterNameAndType> paramList, bool nonVoidIndent)
        {
            string paramName = String.Empty;

            if (!(methodInfo.IsStatic))
            {
                paramName = methParam.MainParamName;
            }
            else
            {
                paramName = MLUtils.GetTypeOutput(methParam.ObjectValue.GetType());
            }

            template.AppendFormat("\t\t{0}{1}.{2}(", (nonVoidIndent) ? "\t" : "", paramName, methodInfo.Name);

            GetParameters(template, paramList, nonVoidIndent, false);

            template.Append(");\r\n");
        }

        /// <summary>
        /// Add parametrs for method
        /// </summary>
        /// <param name="template">output template</param>
        /// <param name="paramList">list of parameters</param>
        /// <param name="nonVoidIndent">non void indent</param>
        /// <param name="IsInvoke">It is for invoke or not</param>
        protected override void GetParameters(StringBuilder template, List<ParameterNameAndType> paramList, bool nonVoidIndent, bool IsInvoke)
        {
            if (paramList.Count > 0)
            {
                template.AppendFormat("\r\n\t\t\t{0}", (nonVoidIndent) ? "\t" : "");
                int count = 0;

                foreach (ParameterNameAndType parameterNameAndType in paramList)
                {
                    if (count > 0)
                    {
                        template.AppendFormat(",\r\n\t\t\t{0}", (nonVoidIndent) ? "\t" : "");
                    }

                    if (!IsInvoke)
                    {
                        if (parameterNameAndType.IsRefParameter)
                        {
                            template.Append("ref ");
                        }
                        else if (parameterNameAndType.IsOutParameter)
                        {
                            template.Append("out ");
                        }
                    }

                    template.Append(parameterNameAndType.Name);

                    count++;
                }
            }
        }

        /// <summary>
        /// Add parameter to Main method on C#
        /// </summary>
        /// <param name="template">output template</param>
        /// <param name="firstParam">is first parameter or not</param>
        /// <param name="paramName">Parameter name</param>
        /// <param name="paramValue">Parameter value</param>
        protected override void AddMainParameter(StringBuilder template, ref bool firstParam, string paramName, object paramValue)
        {
            template.Append("\t\t");
            if (firstParam)
            {
                template.Append(" ");
                firstParam = false;
            }
            else
            {
                template.Append(",");
            }
            
            Type paramType = MLUtils.GetType(paramValue.GetType());

            if (paramValue is MethodInfo)
            {
                paramType = MLUtils.GetType(typeof(MethodInfo));
            }

            mainParams.Add(paramValue);

            //add reference
            AddTypeReferences(paramValue.GetType());

            template.Append(MLUtils.GetTypeOutput(paramType) + " " + paramName + "\r\n");
        }

        #endregion
    }
}
